#include "CSBakeLightVolumes.fxh"

cbuffer cbPerLight
{
	float4 gLightPosWAndRadius;
	float4 gLightColorAndIntens;
	float4 gLightDirAndCosAngle;
#ifdef SHADOWS
	float4x4 gLightViewProj;
#endif
};

#ifdef SHADOWS
SamplerComparisonState ShadowSampler
{
   // sampler state
   AddressU = MIRROR;
   AddressV = MIRROR;

   // sampler comparison state
   ComparisonFunc = LESS;
   Filter = COMPARISON_MIN_MAG_LINEAR_MIP_POINT;
};

Texture2D gShadowMap;

float calculateShadowFactor(float4 lightSpacePos)
{
	lightSpacePos = lightSpacePos / lightSpacePos.w;

	const float depth = lightSpacePos.z;
	const float2 smTexCoord = (lightSpacePos.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f));

	return gShadowMap.SampleCmpLevelZero(ShadowSampler, smTexCoord, depth - 0.000001f);
}
#endif

#ifdef AXES_4
void storeLighting(int3 texCoord, float3 lightStrengthDir, float3 lightColor)
{
	const float3 axis0 = float3(0, 1, 0);
	const float3 axis1 = float3(0, -0.341987, 0.939705);
	const float3 axis2 = float3( 0.813808, -0.341987, -0.469852);
	const float3 axis3 = float3(-0.813808, -0.341987, -0.469852);

	float3 axis0Val, axis1Val, axis2Val, axis3Val;
	read4Axes(texCoord, axis0Val, axis1Val, axis2Val, axis3Val);

	axis0Val += lightColor * max(lightStrengthDir.y, 0); // unnecessary to do dot, since axis0 is standard axis
	axis1Val += lightColor * max(dot(lightStrengthDir, axis1), 0);
	axis2Val += lightColor * max(dot(lightStrengthDir, axis2), 0);
	axis3Val += lightColor * max(dot(lightStrengthDir, axis3), 0);

	write4Axes(texCoord, axis0Val, axis1Val, axis2Val, axis3Val);
}
#else
void storeLighting(int3 texCoord, float3 lightStrengthDir, float3 lightColor)
{
	float3 xPos, xNeg, yPos, yNeg, zPos, zNeg;
	read6Axes(texCoord, xPos, xNeg, yPos, yNeg, zPos, zNeg);

	xPos += lightColor * max( lightStrengthDir.x, 0);
	xNeg += lightColor * max(-lightStrengthDir.x, 0);
	yPos += lightColor * max( lightStrengthDir.y, 0);
	yNeg += lightColor * max(-lightStrengthDir.y, 0);
	zPos += lightColor * max( lightStrengthDir.z, 0);
	zNeg += lightColor * max(-lightStrengthDir.z, 0);

	write6Axes(texCoord, xPos, xNeg, yPos, yNeg, zPos, zNeg);
}
#endif

[numthreads(8, 8, 8)]
void CS(	uint3 GroupID : SV_GroupID,
			uint3 DispatchThreadID : SV_DispatchThreadID,
			uint3 GroupThreadID : SV_GroupThreadID,
			uint GroupIndex : SV_GroupIndex )
{
	const float3 lightPos = gLightPosWAndRadius.xyz;
	const float lightRadius = gLightPosWAndRadius.w;

	const float3 lightColor = gLightColorAndIntens.rgb;
	const float lightIntensity = gLightColorAndIntens.a;

	const float3 spotDir = gLightDirAndCosAngle.xyz;
	const float coneCosAngle = gLightDirAndCosAngle.w;

	float3 lightVolPos = gLightVolPosTex.Load(int2(GroupID.x, 0)).xyz;
	float3 lightVolScale = gLightVolScaleTex.Load(int2(GroupID.x, 0)).xyz;

	int threadOffsetX = gLightVolSectionThreadOffsetXTex.Load(int2(GroupID.x, 0)).x;
	float3 threadPosInSection = float3(DispatchThreadID - int3(threadOffsetX, 0, 0)) + float3(0.5f, 0.5f, 0.5f);

	float3 samplePos = lightVolPos + threadPosInSection * lightVolScale;
	float3 lightDir = lightPos - samplePos;
	float dist = length(lightDir);
	lightDir = normalize(lightDir);

	float spotAngleCos = saturate(dot( -spotDir, lightDir ));

	if(spotAngleCos < coneCosAngle)
		return;

	float atten = saturate((lightRadius - dist) / lightRadius);
	float angleAtten = (spotAngleCos - coneCosAngle) / (1.0 - coneCosAngle);

	float shadowFactor = 1.0f;

#ifdef SHADOWS
	float4 lightSpacePos = mul(float4(samplePos, 1.0f), gLightViewProj);
	shadowFactor = calculateShadowFactor(lightSpacePos);
#endif

	float3 lightStrengthDir = lightDir * atten * angleAtten * lightIntensity * shadowFactor;

	storeLighting(DispatchThreadID, lightStrengthDir, lightColor);

	//float4 xPosValue = rgba8_as_uint_to_float4(gLightXPos[DispatchThreadID]);
	//float4 xNegValue = rgba8_as_uint_to_float4(gLightXNeg[DispatchThreadID]);
	//float4 yPosValue = rgba8_as_uint_to_float4(gLightYPos[DispatchThreadID]);
	//float4 yNegValue = rgba8_as_uint_to_float4(gLightYNeg[DispatchThreadID]);
	//float4 zPosValue = rgba8_as_uint_to_float4(gLightZPos[DispatchThreadID]);
	//float4 zNegValue = rgba8_as_uint_to_float4(gLightZNeg[DispatchThreadID]);

	//gLightXPos[DispatchThreadID] = float4_to_rgba8_as_uint(xPosValue + float4(lightColor * max( lightStrengthDir.x, 0), 0));
	//gLightXNeg[DispatchThreadID] = float4_to_rgba8_as_uint(xNegValue + float4(lightColor * max(-lightStrengthDir.x, 0), 0));
	//gLightYPos[DispatchThreadID] = float4_to_rgba8_as_uint(yPosValue + float4(lightColor * max( lightStrengthDir.y, 0), 0));
	//gLightYNeg[DispatchThreadID] = float4_to_rgba8_as_uint(yNegValue + float4(lightColor * max(-lightStrengthDir.y, 0), 0));
	//gLightZPos[DispatchThreadID] = float4_to_rgba8_as_uint(zPosValue + float4(lightColor * max( lightStrengthDir.z, 0), 0));
	//gLightZNeg[DispatchThreadID] = float4_to_rgba8_as_uint(zNegValue + float4(lightColor * max(-lightStrengthDir.z, 0), 0));

	//gLightXPos[DispatchThreadID] = float4_to_rgba8_as_uint(xPosValue + float4(lightColor * max( dot(lightStrengthDir, gAxis0), 0), 0));
	//gLightXNeg[DispatchThreadID] = float4_to_rgba8_as_uint(xNegValue + float4(lightColor * max( dot(lightStrengthDir, gAxis1), 0), 0));
	//gLightYPos[DispatchThreadID] = float4_to_rgba8_as_uint(yPosValue + float4(lightColor * max( dot(lightStrengthDir, gAxis2), 0), 0));
	//gLightYNeg[DispatchThreadID] = float4_to_rgba8_as_uint(yNegValue + float4(lightColor * max( dot(lightStrengthDir, gAxis3), 0), 0));
}

technique11 tech
{
	pass P0
	{
		SetComputeShader(CompileShader(cs_5_0, CS())); 
	}
}
